//package AP;
import java.io.IOException;
import java.util.Vector;

public class AgentPlatform implements Runnable{
        
        private Vector inAgent = new Vector();
        private Vector inStream = new Vector();
        private Vector outAgent = new Vector();
        private Vector outStream = new Vector();        
        private boolean stop = false;
        private AgentManager agentManager;
        private comms.ConnectionManager connectionManager;
        
        /* constructor */
        public AgentPlatform(AgentManager am){
        	this.agentManager = am;
        	//create Connection Manager and pass it a reference to this Agent Platform object
    		//start Connection Manager thread
        	this.connectionManager = new comms.ConnectionManager(this);
        	this.connectionManager.start();
        }
        
        public static class agentPkt{
                byte[] agentStream;
                String address;
        }
        
        /**
         * waits for agent manager to call setAgent and add 
         * an agent to the outAgent vector or for the connection 
         * manager to call setAgentStream and add an agentPkt to the inStream vector
         */
        public void run() {
                while( !stop ){
                        if( !outAgent.isEmpty() ){
                                try {
                                        this.serialize(this.getOutAgent());
                                } catch (IOException e) {
                                        e.printStackTrace();
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                        if( !inStream.isEmpty() ){
                                try {
                                        this.deserialize(this.getAgentInStream());
                                } catch (IOException e) {
                                        e.printStackTrace();
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                        if( outAgent.isEmpty() && inStream.isEmpty() ){
                                try {
                                        this.wait();
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        }
                                
                }
        }
        
        /********************************************************************************/
        /**
         * method called by the agent manager. Adds a MobileAgent to the outAgent vector
         * and then notifies this thread that an agent is to be sent
         */
        public synchronized void setAgent(MobileAgent a) {
                this.outAgent.addElement(a);
                this.notify();
        }       
        
        /**
         * method called locally. Removes a MobileAgent 
         * from the outAgent vector if it is not empty
         */
        private synchronized MobileAgent getOutAgent() throws InterruptedException {
                while( outAgent.isEmpty() ){
                        try {
                                this.wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }                       
                }
                MobileAgent agent = (MobileAgent) this.outAgent.elementAt(0);
                this.outAgent.removeElementAt(0);
                return agent;
        }
        
        /**
         * method called locally. Creates an agentPkt, sets agentStream using
         * the persist method, and sets the destination address.
         * It then adds the agentPkt to the outStream vector and notifies the 
         * connection manager that there is an agent waiting to be sent  
         */
    private void serialize(MobileAgent agent) throws IOException {
        agentPkt pkt = new agentPkt();
        pkt.agentStream = agent.persist();
        pkt.address = agent.getNextDest();
        this.outStream.addElement(pkt);
        this.connectionManager.notify();
    }
    
        /**
         * method called by the connection manager. removes an agentPkt
         * from the outStream vector and sends it to the next agent.     
         */
        public synchronized agentPkt getAgentPkt() throws InterruptedException {
                while( outStream.isEmpty() ){
                        try {
                                this.connectionManager.wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }                       
                }
                agentPkt pkt = (agentPkt) this.outStream.elementAt(0);
                this.outStream.removeElementAt(0);
                return pkt;
        }
        /********************************************************************************/
        /**
         * method called by the connection manager. Adds an agentPkt to the inStream vector
         * and then notifies the thread that an agent has been received
         */
        public synchronized void setAgentStream(agentPkt pkt) {
                this.inStream.addElement(pkt);
                this.notify();
        }       
        
        /**
         * method called locally. Removes an agentPkt from 
         * the inStream vector if it is not empty
         */
        private synchronized agentPkt getAgentInStream() throws InterruptedException {
                while( inStream.isEmpty() ){
                        try {
                                this.wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }                       
                }
                agentPkt pkt = (agentPkt) this.inStream.elementAt(0);
                this.inStream.removeElementAt(0);
                return pkt;
        }
        
        /**
         * method called locally. Creates a new agent and using 
         * the resurrect method sets the agent members to the 
         * values received. It then adds the agent to the inAgent
         * vector and calls notify on the agent manager
         */
    public void deserialize( agentPkt pkt ) throws IOException {
        MobileAgent agent = new MobileAgent();   
        agent.resurrect(pkt.agentStream);
        this.inAgent.addElement(agent);
        this.agentManager.notify();
        }

        /**
         * method called by the agent manager. removes 
         * agent from the inAgent vector if not empty
         */
        public synchronized MobileAgent getAgent() throws InterruptedException {
                while( inAgent.isEmpty() ){
                        try {
                                this.agentManager.wait();
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }                       
                }
                MobileAgent agent = (MobileAgent) this.inAgent.elementAt(0);
                this.inAgent.removeElementAt(0);
                return agent;
        }
        /********************************************************************************/      
        /**
         * method used to stop the while loop in the run method
         * and also the connection manager thread that was started
         * in the constructor.
         */
        public synchronized void stop(){
        	this.connectionManager.stop();
        	this.stop = true;
            this.notify();          
        }
}
